/*
 * Copyright (c) 2023-2025 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.mybatis.ext.sql;

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.apache.commons.compress.utils.Lists;
import org.elsfs.cloud.common.core.entity.BaseEntity;
import org.elsfs.cloud.common.mybatis.ext.sql.support.builder.StandardSelectSql;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.FieldEnumResolverSupport;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.HierarchyTableAliasManager;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.SimpleTableAliasManager;
import org.elsfs.cloud.common.mybatis.ext.sql.support.condition.ConditionItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.ExpressionSelectItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.SelectItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.StandardSelectItem;
import org.elsfs.cloud.common.mybatis.ext.sql.support.select.SubQuerySelectItem;
import org.elsfs.cloud.common.util.lang.StringUtils;

/**
 * 抽象查询Join实现
 *
 * @param <C> 子类具体类型
 */
@SuppressWarnings("unchecked")
public abstract class AbstractSelectJoin<C extends AbstractSelectJoin<C>>
    extends CompositeSqlFragment
    implements Join<
            C,
            AbstractSelectJoin<C>.SelectJoinBuilderImpl,
            AbstractSelectJoin<C>.SelectJoinBuilderImpl.JoinConditionBuilderImpl>,
        SqlParameterManagerAware,
        TableAliasManagerAware {

  /** 具体子类实现引用 */
  protected final C childThis = (C) this;

  /** Join构建器列表 */
  private final List<SelectJoinBuilderImpl> joinBuilders = Lists.newArrayList();

  /**
   * 内连接一张表
   *
   * @param entityClass 实体类
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl join(Class<? extends BaseEntity> entityClass) {
    SelectJoinBuilderImpl joinBuilder =
        new SelectJoinBuilderImpl(
            JoinType.INNER_JOIN, TableInfoHelper.getTableInfo(entityClass).getTableName());
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl join(Class<? extends BaseEntity> entityClass, String tableAlias) {
    SelectJoinBuilderImpl joinBuilder =
        new SelectJoinBuilderImpl(
            JoinType.INNER_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl join(
      Class<? extends BaseEntity> entityClass, String tableAlias, boolean registerAlias) {
    SelectJoinBuilderImpl joinBuilder =
        new SelectJoinBuilderImpl(
            JoinType.INNER_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias,
            registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一张表
   *
   * @param table 数据库表名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl join(String table) {
    SelectJoinBuilderImpl joinBuilder = new SelectJoinBuilderImpl(JoinType.INNER_JOIN, table);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl join(String table, String tableAlias) {
    SelectJoinBuilderImpl joinBuilder =
        new SelectJoinBuilderImpl(JoinType.INNER_JOIN, table, tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl join(String table, String tableAlias, boolean registerAlias) {
    SelectJoinBuilderImpl joinBuilder =
        new SelectJoinBuilderImpl(JoinType.INNER_JOIN, table, tableAlias, registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一个子查询
   *
   * @param sqlBuilderConsumer 子查询构建器
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl join(
      Consumer<StandardSelectSql> sqlBuilderConsumer, String tableAlias) {
    return this.doSubQueryJoin(sqlBuilderConsumer, tableAlias, JoinType.INNER_JOIN);
  }

  /**
   * 左连接一张表
   *
   * @param entityClass 实体类
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl leftJoin(Class<? extends BaseEntity> entityClass) {
    SelectJoinBuilderImpl joinBuilder =
        new SelectJoinBuilderImpl(
            JoinType.LEFT_JOIN, TableInfoHelper.getTableInfo(entityClass).getTableName());
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl leftJoin(
      Class<? extends BaseEntity> entityClass, String tableAlias) {
    SelectJoinBuilderImpl joinBuilder =
        new SelectJoinBuilderImpl(
            JoinType.LEFT_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl leftJoin(
      Class<? extends BaseEntity> entityClass, String tableAlias, boolean registerAlias) {
    SelectJoinBuilderImpl joinBuilder =
        new SelectJoinBuilderImpl(
            JoinType.LEFT_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias,
            registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一张表
   *
   * @param table 数据库表名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl leftJoin(String table) {
    SelectJoinBuilderImpl joinBuilder = new SelectJoinBuilderImpl(JoinType.LEFT_JOIN, table);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl leftJoin(String table, String tableAlias) {
    SelectJoinBuilderImpl joinBuilder =
        new SelectJoinBuilderImpl(JoinType.LEFT_JOIN, table, tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl leftJoin(String table, String tableAlias, boolean registerAlias) {
    SelectJoinBuilderImpl joinBuilder =
        new SelectJoinBuilderImpl(JoinType.LEFT_JOIN, table, tableAlias, registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一个子查询
   *
   * @param sqlBuilderConsumer 子查询构建器
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl leftJoin(
      Consumer<StandardSelectSql> sqlBuilderConsumer, String tableAlias) {
    return this.doSubQueryJoin(sqlBuilderConsumer, tableAlias, JoinType.LEFT_JOIN);
  }

  /**
   * 右连接一张表
   *
   * @param entityClass 实体类
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl rightJoin(Class<? extends BaseEntity> entityClass) {
    SelectJoinBuilderImpl joinBuilder =
        new SelectJoinBuilderImpl(
            JoinType.RIGHT_JOIN, TableInfoHelper.getTableInfo(entityClass).getTableName());
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl rightJoin(
      Class<? extends BaseEntity> entityClass, String tableAlias) {
    SelectJoinBuilderImpl joinBuilder =
        new SelectJoinBuilderImpl(
            JoinType.RIGHT_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl rightJoin(
      Class<? extends BaseEntity> entityClass, String tableAlias, boolean registerAlias) {
    SelectJoinBuilderImpl joinBuilder =
        new SelectJoinBuilderImpl(
            JoinType.RIGHT_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias,
            registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一张表
   *
   * @param table 数据库表名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl rightJoin(String table) {
    SelectJoinBuilderImpl joinBuilder = new SelectJoinBuilderImpl(JoinType.RIGHT_JOIN, table);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl rightJoin(String table, String tableAlias) {
    SelectJoinBuilderImpl joinBuilder =
        new SelectJoinBuilderImpl(JoinType.RIGHT_JOIN, table, tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl rightJoin(String table, String tableAlias, boolean registerAlias) {
    SelectJoinBuilderImpl joinBuilder =
        new SelectJoinBuilderImpl(JoinType.RIGHT_JOIN, table, tableAlias, registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一个子查询
   *
   * @param sqlBuilderConsumer 子查询构建器
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public SelectJoinBuilderImpl rightJoin(
      Consumer<StandardSelectSql> sqlBuilderConsumer, String tableAlias) {
    return this.doSubQueryJoin(sqlBuilderConsumer, tableAlias, JoinType.RIGHT_JOIN);
  }

  /**
   * 子查询JOIN
   *
   * @param sqlBuilderConsumer 子查询SQL构建器
   * @param tableAlias 临时表别名
   * @param joinType 连接类型
   * @return 连接配置对象
   */
  private SelectJoinBuilderImpl doSubQueryJoin(
      Consumer<StandardSelectSql> sqlBuilderConsumer, String tableAlias, JoinType joinType) {
    if (StringUtils.isBlank(tableAlias)) {
      throw new MybatisPlusException("JOIN子查询的表别名不能为空");
    }

    StandardSelectSql standardSelectSql =
        new StandardSelectSql(
            this.getSqlParameterManager(),
            new HierarchyTableAliasManager(
                new SimpleTableAliasManager(), this.getTableAliasManager()));
    sqlBuilderConsumer.accept(standardSelectSql);

    SelectJoinBuilderImpl joinBuilder =
        new SelectJoinBuilderImpl(
            joinType, "(" + standardSelectSql.buildSqlFragment() + "(", tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /** 构建SQL之前处理 */
  @Override
  protected void beforeBuild() {
    for (SelectJoinBuilderImpl joinBuilder : this.joinBuilders) {
      this.addSqlFragment(joinBuilder);
    }
  }

  /**
   * 添加选择项
   *
   * @param selectItem 选择项
   */
  protected abstract void addSelectItem(SelectItem selectItem);

  /** 查询Join构建器实现 */
  public class SelectJoinBuilderImpl extends CompositeSqlFragment
      implements SelectJoinBuilder<
              SelectJoinBuilderImpl, SelectJoinBuilderImpl.JoinConditionBuilderImpl, C>,
          SqlParameterManagerAware,
          TableAliasManagerAware {

    /** 连接类型 */
    private final JoinType joinType;

    /** 连接表名称 */
    private final String table;

    /** 连接表别名 */
    private final String tableAlias;

    /** Join条件构建器 */
    private JoinConditionBuilderImpl joinConditionBuilder;

    /**
     * 构造方法
     *
     * @param joinType 连接类型
     * @param table 表名称
     * @param tableAlias 表别名
     * @param registerAlias 是否注册别名
     */
    public SelectJoinBuilderImpl(
        JoinType joinType, String table, String tableAlias, boolean registerAlias) {
      this.joinType = joinType;
      this.table = table;
      this.tableAlias = tableAlias;

      if (registerAlias) {
        AbstractSelectJoin.this.getTableAliasManager().registerAlias(table, tableAlias);
      }
    }

    /**
     * 构造方法
     *
     * @param joinType 连接类型
     * @param table 表名称
     * @param tableAlias 表别名
     */
    public SelectJoinBuilderImpl(JoinType joinType, String table, String tableAlias) {
      this(joinType, table, tableAlias, true);
    }

    /**
     * 构造方法
     *
     * @param joinType 连接类型
     * @param table 表名称
     */
    public SelectJoinBuilderImpl(JoinType joinType, String table) {
      this(
          joinType,
          table,
          AbstractSelectJoin.this.getTableAliasManager().generateAlias(table),
          true);
    }

    /**
     * 解析字段枚举名称
     *
     * @param field 字段枚举
     * @return 字段名称
     */
    protected String resolveFieldName(Enum<?> field) {
      return FieldEnumResolverSupport.resolveFieldName(this.getTableAliasManager(), field);
    }

    /**
     * 返回上一级对象
     *
     * @return 上级对象
     */
    @Override
    public C end() {
      return AbstractSelectJoin.this.childThis;
    }

    /**
     * 连接条件设置
     *
     * @return 连接条件构建器
     */
    @Override
    public JoinConditionBuilderImpl on() {
      this.joinConditionBuilder = new JoinConditionBuilderImpl();
      return this.joinConditionBuilder;
    }

    /** 构建SQL之前处理 */
    @Override
    protected void beforeBuild() {
      this.addSqlFragment(new TokenSqlFragment(this.joinType.getJoinKeyword()));
      this.addSqlFragment(new TokenSqlFragment(this.table + " AS " + this.tableAlias));
      this.addSqlFragment(this.joinConditionBuilder);
    }

    /**
     * 获取SQL参数管理器
     *
     * @return SQL参数管理器
     */
    @Override
    public SqlParameterManager getSqlParameterManager() {
      return AbstractSelectJoin.this.getSqlParameterManager();
    }

    /**
     * 选择关联SQL字段
     *
     * @param fields SQL字段
     * @return 具体实现
     */
    @Override
    public SelectJoinBuilderImpl select(String... fields) {
      for (String field : fields) {
        this.addSelectField(field);
      }

      return this;
    }

    /**
     * 选择关联SQL字段
     *
     * @param fields SQL字段枚举
     * @return 具体实现
     */
    @Override
    public SelectJoinBuilderImpl select(Enum<?>... fields) {
      for (Enum<?> field : fields) {
        this.addSelectField(this.resolveFieldName(field));
      }

      return this;
    }

    /**
     * 选择关联SQL字段
     *
     * @param fields SQL字段
     * @return 具体实现
     */
    @Override
    public SelectJoinBuilderImpl select(Collection<String> fields) {
      for (String field : fields) {
        this.addSelectField(field);
      }

      return this;
    }

    /**
     * 选择关联SQL字段表达式
     *
     * @param expressions SQL字段表达式
     * @return 具体实现
     */
    @Override
    public SelectJoinBuilderImpl selectExpression(String... expressions) {
      for (String expression : expressions) {
        this.addSelectExpression(expression);
      }

      return this;
    }

    /**
     * 选择关联SQL字段表达式
     *
     * @param expressions SQL字段表达式
     * @return 具体实现
     */
    @Override
    public SelectJoinBuilderImpl selectExpression(Collection<String> expressions) {
      for (String expression : expressions) {
        this.addSelectExpression(expression);
      }

      return this;
    }

    /**
     * 选择SQL字段
     *
     * @param field SQL字段
     * @param fieldAlias 字段别名
     * @return 具体实现
     */
    @Override
    public SelectJoinBuilderImpl selectAs(String field, String fieldAlias) {
      if (StringUtils.isBlank(fieldAlias)) {
        throw new MybatisPlusException("选择字段" + field + "的别名不能为空");
      }
      this.addSelectField(field + " AS " + fieldAlias);

      return this;
    }

    /**
     * 选择SQL字段
     *
     * @param field SQL字段枚举
     * @param fieldAlias 字段别名
     * @return 具体实现
     */
    @Override
    public SelectJoinBuilderImpl selectAs(Enum<?> field, String fieldAlias) {
      return this.selectAs(this.resolveFieldName(field), fieldAlias);
    }

    /**
     * 选择SQL字段
     *
     * @param field SQL字段枚举
     * @param fieldAlias 字段别名枚举
     * @return 具体实现
     */
    @Override
    public SelectJoinBuilderImpl selectAs(Enum<?> field, Enum<?> fieldAlias) {
      return this.selectAs(field, fieldAlias.name());
    }

    /**
     * 选择子查询结果
     *
     * @param sqlBuilderConsumer SQL构建器消费器
     * @param fieldAlias 字段别名
     * @return 具体实现
     */
    @Override
    public SelectJoinBuilderImpl selectAs(
        Consumer<StandardSelectSql> sqlBuilderConsumer, String fieldAlias) {
      if (StringUtils.isBlank(fieldAlias)) {
        throw new MybatisPlusException("选择子查询的字段别名不能为空");
      }

      StandardSelectSql standardSelectSql =
          new StandardSelectSql(
              this.getSqlParameterManager(),
              new HierarchyTableAliasManager(
                  new SimpleTableAliasManager(), this.getTableAliasManager()));
      sqlBuilderConsumer.accept(standardSelectSql);
      this.addSelectSubQuery(standardSelectSql, fieldAlias);
      return this;
    }

    /**
     * 选择子查询结果
     *
     * @param sqlBuilderConsumer SQL构建器消费器
     * @param fieldAlias 字段别名枚举
     * @return 具体实现
     */
    @Override
    public SelectJoinBuilderImpl selectAs(
        Consumer<StandardSelectSql> sqlBuilderConsumer, Enum<?> fieldAlias) {
      return this.selectAs(sqlBuilderConsumer, fieldAlias.name());
    }

    /**
     * 添加选择SQL字段
     *
     * @param field SQL字段
     */
    private void addSelectField(String field) {
      StandardSelectItem selectItem = new StandardSelectItem(field);
      if (StringUtils.isBlank(selectItem.getTableAlias())) {
        selectItem =
            new StandardSelectItem(
                this.tableAlias, selectItem.getField(), selectItem.getFieldAlias());
      }

      AbstractSelectJoin.this.addSelectItem(selectItem);
    }

    /**
     * 添加选择子查询
     *
     * @param selectSql 子查询SQL
     * @param fieldAlias 字段别名
     */
    private void addSelectSubQuery(StandardSelectSql selectSql, String fieldAlias) {
      AbstractSelectJoin.this.addSelectItem(
          new SubQuerySelectItem(selectSql.buildSqlFragment(), fieldAlias));
    }

    /**
     * 添加选择SQL字段表达式
     *
     * @param expression SQL字段表达式
     */
    private void addSelectExpression(String expression) {
      AbstractSelectJoin.this.addSelectItem(new ExpressionSelectItem(expression));
    }

    /**
     * 选择关联SQL字段
     *
     * @param fields SQL字段枚举
     * @return 具体实现
     */
    @Override
    public SelectJoinBuilderImpl selectEnum(Collection<Enum<?>> fields) {
      for (Enum<?> field : fields) {
        this.addSelectField(this.resolveFieldName(field));
      }

      return this;
    }

    /**
     * 获取表别名管理器
     *
     * @return 表名别管理器
     */
    @Override
    public TableAliasManager getTableAliasManager() {
      return AbstractSelectJoin.this.getTableAliasManager();
    }

    /** Join条件构建器实现 */
    public class JoinConditionBuilderImpl extends AbstractCondition<JoinConditionBuilderImpl>
        implements JoinConditionBuilder<JoinConditionBuilderImpl, SelectJoinBuilderImpl, C> {

      /** Join条件列表 */
      private final List<ConditionItem> conditionItems = Lists.newArrayList();

      /**
       * 获取SQL参数管理器
       *
       * @return SQL参数管理器
       */
      @Override
      public SqlParameterManager getSqlParameterManager() {
        return SelectJoinBuilderImpl.this.getSqlParameterManager();
      }

      /**
       * 添加一个条件
       *
       * @param conditionItem 条件
       */
      @Override
      protected void addConditionItem(ConditionItem conditionItem) {
        this.conditionItems.add(conditionItem);
      }

      /**
       * 返回上一级对象
       *
       * @return 上级对象
       */
      @Override
      public C end() {
        return SelectJoinBuilderImpl.this.end();
      }

      /**
       * 构建SQL片段
       *
       * @return SQL片段
       */
      @Override
      public String buildSqlFragment() {
        if (this.conditionItems.isEmpty()) {
          return StringUtils.EMPTY;
        }

        return String.format(
            "ON %s",
            ConditionItem.stripHeaderKeyword(
                this.conditionItems.stream()
                    .map(ConditionItem::buildSqlFragment)
                    .collect(Collectors.joining(StringUtils.SPACE))));
      }

      /**
       * 获取表别名管理器
       *
       * @return 表名别管理器
       */
      @Override
      public TableAliasManager getTableAliasManager() {
        return SelectJoinBuilderImpl.this.getTableAliasManager();
      }
    }
  }
}
